package com.tipfy.tmx;


class TiledMap
{
    /**
     * required
     */
    public var version:String;

    /**
     * Required. Must be "orthogonal", "isometric", "hexagonal" or "shifted".
     */
    public var orientation:String;

    /**
     * required, nonNegativeInteger
     */
    public var width:Int;

    /**
     * required, nonNegativeInteger
     */
    public var height:Int;

    /**
     * required, nonNegativeInteger
     */
    public var tilewidth:Int;

    /**
     * required, nonNegativeInteger
     */
    public var tileheight:Int;

    /**
     *
     */
    public var properties:TiledProperties;

    /**
     *
     */
    public var tilesets:Array<TiledTileSet>;

    /**
     *
     */
    public var layers:Array<TiledLayer>;

    /**
     *
     */
    public var objectGroups:Array<TiledObjectGroup>;

    /**
     * Constructor.
     */
    public function new(xml:Xml)
    {
        // XXX
        var validOrientation:Array<String> = this._getValidOrientation();

        // Attributes.
        this.version = TiledAttributes.get(xml, "version");
        this.orientation = TiledAttributes.getEnum(xml, "orientation",
            validOrientation);
        this.width = TiledAttributes.getNonNegativeInt(xml, "width");
        this.height = TiledAttributes.getNonNegativeInt(xml, "height");
        this.tilewidth = TiledAttributes.getNonNegativeInt(xml, "tilewidth");
        this.tileheight = TiledAttributes.getNonNegativeInt(xml, "tileheight");

        // Child elements.
        for (elem in xml.elements())
        {
            switch (elem.nodeName)
            {
                case "properties":
                    this._loadProperties(elem);
                case "tileset":
                    this._loadTileset(elem);
                case "layer":
                    this._loadLayer(elem);
                case "objectgroup":
                    this._loadObjectGroup(elem);
                default:
                    throw "Invalid element <" + elem.nodeName + "> in <map>";
            }
        }

        // Some elements must occur at least once.
        if (this.tilesets == null)
        {
            throw "Missing element <tileset>.";
        }

        if (this.layers == null && this.objectGroups == null)
        {
            throw "Missing element <layer> or <objectgroup>.";
        }
    }

    /**
     *
     */
    public static function fromResource(name:String):TiledMap
    {
        var xmlString:String = haxe.Resource.getString(name);
        return new TiledMap(Xml.parse(xmlString).firstElement());
    }

    /**
     * Which other way to validate possible enum values?
     */
    private inline function _getValidOrientation():Array<String>
    {
        var res:Array<String> = new Array<String>();
        res.push("orthogonal");
        res.push("isometric");
        res.push("hexagonal");
        res.push("shifted");
        return res;
    }

    /**
     * <properties>. minOccurs="0", maxOccurs="1"
     */
    private function _loadProperties(xml:Xml):Void
    {
        if (this.properties != null)
        {
            throw "Duplicated <properties> tag.";
        }

        this.properties = new TiledProperties(xml);
    }

    /**
     * <tileset>. minOccurs="1", maxOccurs="unbounded"
     */
    private function _loadTileset(xml:Xml):Void
    {
        if (this.tilesets == null)
        {
            this.tilesets = new Array<TiledTileSet>();
        }

        this.tilesets.push(new TiledTileSet(xml));
    }

    /**
     * <layer>. minOccurs="1" (or 1 objectgroup), maxOccurs="unbounded"
     */
    private function _loadLayer(xml:Xml):Void
    {
        if (this.layers == null)
        {
            this.layers = new Array<TiledLayer>();
        }

        this.layers.push(new TiledLayer(xml));
    }

    /**
     * <objectgroup>. minOccurs="1" (or 1 layer), maxOccurs="unbounded"
     */
    private function _loadObjectGroup(xml:Xml):Void
    {
        if (this.objectGroups == null)
        {
            this.objectGroups = new Array<TiledObjectGroup>();
        }

        this.objectGroups.push(new TiledObjectGroup(xml));
    }
}
